package com.tools.common.container;

import com.tools.common.container.r.OnlyReadArrayList;
import com.tools.common.container.r.OnlyReadHashSet;
import com.tools.common.container.r.OnlyReadLinkedSet;
import com.tools.common.container.r.OnlyReadTreeSet;
import com.tools.common.exception.InvalidOperationException;
import com.tools.common.object.Note;
import org.jetbrains.annotations.NotNull;

import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ThreadLocalRandom;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.function.UnaryOperator;
import java.util.stream.Stream;

/**
 * 自定义 List 集合容器的
 * 抽象类实现，对 OwnRow 接口
 * 的方法做了默认实现，且兼容和扩展了
 * 原生 List 集合的 API，使该自定义
 * 的 List 容器被继承的子类的代码结构
 * 更加干净整洁
 * */
@Note("自定义 List 集合容器的抽象类实现，对 OwnRow 接口" +
        "的方法做了默认实现，且兼容和扩展了原生 List 集合的 API，" +
        "使该自定义的 List 容器被继承的子类的代码结构更加干净整洁")
public abstract class AbsOwnList<T> implements OwnRows<T> {

    @Note("数据存放的容器实体，使用 List 集合做包装")
    private List<T> body;

    @Note("针对 body 属性，使用它的哪个实现类实现它，默认是 ArrayList 集合")
    private ListEnum implEnum = ListEnum.ARRAY;

    @Note("如果 body 不为空，是否需要对数据内容做排序，这里用于保存排序的比较器")
    private Comparator<? super T> comparator;

    /* *******************************************************************************************
     *
     *          构造器
     *
     * *******************************************************************************************
     * */

    public AbsOwnList() {
        this.body = this.implEnum.newList(0);
    }

    public AbsOwnList(int initSize) {
        this.body = this.implEnum.newList(initSize);
    }

    public AbsOwnList(T[] array) {
        private_initBody(array, null, false, this.implEnum);
    }

    public AbsOwnList(Comparator<? super T> c, T[] array) {
        this.comparator = c;
        private_initBody(array, c, true, this.implEnum);
    }

    public AbsOwnList(Iterable<? extends T> iterable) {
        private_initBody(iterable, null, false, this.implEnum);
    }


    public AbsOwnList(Iterable<? extends T> iterable, Comparator<? super T> c) {
        this.comparator = c;
        private_initBody(iterable, c, true, this.implEnum);
    }

    public AbsOwnList(ListEnum implEnum, int initSize) {
        private_implNotNull(implEnum);
        this.implEnum = implEnum;
        this.body = this.implEnum.newList(initSize);
    }


    public AbsOwnList(ListEnum implEnum, T[] array) {
        private_implNotNull(implEnum);
        private_initBody(array, null, false, implEnum);
    }


    public AbsOwnList(ListEnum implEnum, Comparator<? super T> c, T[] array) {
        private_implNotNull(implEnum);
        private_initBody(array, c, true, implEnum);
        this.comparator = c;
    }

    public AbsOwnList(ListEnum implEnum, Iterable<? extends T> iterable) {
        private_implNotNull(implEnum);
        private_initBody(iterable, null,false, implEnum);
    }

    public AbsOwnList(ListEnum implEnum, Iterable<? extends T> iterable, Comparator<? super T> c) {
        private_implNotNull(implEnum);
        private_initBody(iterable, c,true, implEnum);
        this.comparator = c;
    }

    /* *******************************************************************************************
     *
     *          链式增删改方法
     *
     * *******************************************************************************************
     * */

    @Override
    public AbsOwnList<T> add(T obj) {
        this.body.add(obj);
        return this;
    }

    public AbsOwnList<T> add(int index, T obj) {
        private_checkAllowAddIndex(index);
        this.body.add(index, obj);
        return this;
    }

    @Override
    public AbsOwnList<T> addAll(T[] objs) {
        if(objs != null && objs.length != 0) {
            if(implEnum == ListEnum.COPY_WRITE) {
                this.body.addAll(Arrays.asList(objs));
                return this;
            }
            Collections.addAll(body, objs);
        }
        return this;
    }

    public AbsOwnList<T> addAll(int index, T[] array) {
        if(array == null || array.length == 0) return this;
        private_checkAllowAddIndex(index);
        if(implEnum == ListEnum.COPY_WRITE) {
            this.body.addAll(Arrays.asList(array));
            return this;
        }
        Collections.addAll(this.body, array);
        return this;
    }

    @Override
    public AbsOwnList<T> addAll(Iterable<? extends T> objs) {
        if(objs != null) {
            for (T obj : objs) body.add(obj);
        }
        return this;
    }

    public AbsOwnList<T> addAll(int index, Collection<? extends T> objs) {
        if(objs == null || objs.isEmpty()) return this;
        private_checkAllowAddIndex(index);
        this.body.addAll(index, objs);
        return this;
    }

    public AbsOwnList<T> addAll(int index, OwnRows<? extends T> objs) {
        if(objs == null || objs.isEmpty()) return this;
        private_checkAllowAddIndex(index);
        this.body.addAll(index, objs.toCollection());
        return this;
    }

    public AbsOwnList<T> addFirst(T obj) {
        if(body instanceof LinkedList) {
            ((LinkedList<T>) body).addFirst(obj);
            return this;
        }
        this.body.add(0, obj);
        return this;
    }

    public AbsOwnList<T> remove(int index) {
        private_checkReadIndex(index);
        this.body.remove(index);
        return this;
    }

    @Override
    public AbsOwnList<T> remove(Object obj) {
        this.body.remove(obj);
        return this;
    }

    @Override
    public AbsOwnList<T> removeAll(Object[] objs) {
        if(objs == null ||  objs.length == 0) return this;
        for(Object o : objs) this.body.remove(o);
        return this;
    }

    @Override
    public AbsOwnList<T> removeAll(Iterable<?> objs) {
        if(objs == null) return this;
        for(Object o : objs) this.body.remove(o);
        return this;
    }


    public AbsOwnList<T> set(int index, T newValue) {
        private_checkAllowAddIndex(index);
        this.body.set(index, newValue);
        return this;
    }


    public AbsOwnList<T> setFirst(T newValue) {
        if(body.isEmpty()) {
            this.body.add(newValue);
            return this;
        }
        this.body.set(0, newValue);
        return this;
    }


    public AbsOwnList<T> setLast(T newValue) {
        if(body.isEmpty()) {
            this.body.add(newValue);
            return this;
        }
        int last = this.body.size() - 1;
        this.body.set(last, newValue);
        return this;
    }

    /* *******************************************************************************************
     *
     *          接口方法
     *
     * *******************************************************************************************
     * */

    @Override
    public T get(int index) {
        private_checkReadIndex(index);
        return this.body.get(index);
    }

    @Override
    public T getOrDefault(int index, T defaultValue) {
        return (index < 0 || index >= body.size()) ? defaultValue : this.body.get(index);
    }

    @Override
    public int size() {
        return this.body.size();
    }

    @Override
    public void clear() {
        this.body.clear();
    }

    @Override
    public T random() {
        if(body.isEmpty()) return null;
        return this.body.get(ThreadLocalRandom.current().nextInt(this.body.size()));
    }

    @Override
    public String toString() {
        return body.toString();
    }

    @Override
    public int hashCode() {
        return body.hashCode();
    }

    @Override
    public boolean equals(Object o) {
        if(o == this) return true;
        if(o instanceof AbsOwnList) {
            return this.body.equals(((AbsOwnList<?>) o).body);
        }
        return false;
    }

    @Override
    public Comparator<? super T> getComparator() {
        return this.comparator;
    }



    @Override
    public Collection<T> toCollection() {
        return this.implEnum.newList(this.body);
    }

    @Override
    public List<T> toList(ListEnum listEnum) {
        return (listEnum == null)
                ? new ArrayList<>(body)
                : listEnum.newList(body);
    }

    @Override
    public Set<T> toSet(SetEnum setEnum) {
        return (setEnum == null)
                ? new HashSet<>(body)
                : setEnum.newSet(body);
    }

    @Override
    public void copyToCollection(Collection<T> target) {
        if(target == null) return;
        target.addAll(body);
    }

    @Override
    public T[] toArray(Class<T> valueType) {
        int length = body.size();
        Object target = Array.newInstance(valueType, length);
        for (int i = 0; i < length; i++) {
            Array.set(target, i, body.get(i));
        }
        return (T[]) target;
    }

    @Override
    public Object[] toArray() {
        return body.toArray();
    }

    @Override
    public OnlyReadArrayList<T> toOnlyReadArrayList() {
        return new OnlyReadArrayList<>(this.body);
    }

    @Override
    public OnlyReadHashSet<T> toOnlyReadHashSet() {
        return new OnlyReadHashSet<>(this.body);
    }

    @Override
    public OnlyReadLinkedSet<T> toOnlyReadLinkedSet() {
        return new OnlyReadLinkedSet<>(this.body);
    }

    @Override
    public OnlyReadTreeSet<T> toOnlyReadTreeSet() {
        return new OnlyReadTreeSet<>(comparator, this.body);
    }

    @Override
    public OnlyReadTreeSet<T> toOnlyReadTreeSet(Comparator<? super T> comparator) {
        return new OnlyReadTreeSet<>(comparator, this.body);
    }

    @NotNull
    @Override
    public Iterator<T> iterator() {
        return this.body.iterator();
    }

    @Override
    public void forEach(Consumer<? super T> action) {
        this.body.forEach(action);
    }

    @Override
    public Spliterator<T> spliterator() {
        return this.body.spliterator();
    }

    @Override
    public boolean contains(T obj) {
        return body.contains(obj);
    }


    @Override
    public final boolean containsAll(Object... objs) {
        if(objs == null || objs.length == 0) return false;
        for(Object o : objs) if(!body.contains(o)) return false;
        return true;
    }

    @Override
    public final boolean containsAll(Collection<?> objs) {
        if(objs == null || objs.isEmpty()) return false;
        for(Object o : objs) if(!body.contains(o)) return false;
        return true;
    }

    @Override
    public final boolean containsAll(OwnRows<?> objs) {
        if(objs == null || objs.isEmpty()) return false;
        for(Object o : objs) if(!body.contains(o)) return false;
        return true;
    }

    @Override
    public boolean containsAll(Iterable<?> objs) {
        if(objs == null) return false;
        Iterator<?> iterator = objs.iterator();
        if(!iterator.hasNext()) return false;
        while (iterator.hasNext()) {
            if(!body.contains(iterator.next())) return false;
        }
        return true;
    }


    @Override
    public final boolean containsAny(Object... objs) {
        if(objs == null || objs.length == 0) return false;
        for(Object o : objs) if(body.contains(o)) return true;
        return false;
    }

    @Override
    public final boolean containsAny(Collection<?> objs) {
        if(objs == null || objs.isEmpty()) return false;
        for(Object o : objs) if(body.contains(o)) return true;
        return false;
    }

    @Override
    public final boolean containsAny(OwnRows<?> objs) {
        if(objs == null || objs.isEmpty()) return false;
        for(Object o : objs) if(body.contains(o)) return true;
        return false;
    }

    @Override
    public boolean containsAny(Iterable<?> objs) {
        if(objs == null) return false;
        Iterator<?> iterator = objs.iterator();
        if(!iterator.hasNext()) return false;
        while (iterator.hasNext()) {
            if(body.contains(iterator.next())) return true;
        }
        return false;
    }

    @Override
    public void sort(Comparator<? super T> comparator) {
        this.comparator = comparator;
        if(!body.isEmpty()) this.body.sort(this.comparator);
    }

    @Override
    public boolean isEmpty() {
        return body == null || body.isEmpty();
    }

    /* *******************************************************************************************
     *
     *          其他自定义方法
     *
     * *******************************************************************************************
     * */

    public ListEnum getImplEnum() {
        return this.implEnum;
    }

    public List<T> toList() {
        return this.implEnum.newList(body);
    }

    public ListIterator<T> listIterator() {
        return body.listIterator();
    }

    public ListIterator<T> listIterator(int index) {
        private_checkReadIndex(index);
        return body.listIterator(index);
    }

    public int indexOf(T obj) {
        return body.indexOf(obj);
    }

    public int lastIndexOf(T obj) {
        return body.lastIndexOf(obj);
    }

    public T getFirst() {
        if(body.isEmpty()) return null;
        return body.get(0);
    }

    public T getLast() {
        if(body.isEmpty()) return null;
        int index = this.body.size() - 1;
        return body.get(index);
    }

    public List<T> subList(int fromIndex, int toIndex) {
        protected_checkRangeIndex(fromIndex, toIndex);
        return body.subList(fromIndex, toIndex);
    }

    public boolean removeIf(Predicate<? super T> filter) {
        return body.removeIf(filter);
    }

    public void replaceAll(UnaryOperator<T> operator) {
        this.body.replaceAll(operator);
    }

    public boolean retainAll(Collection<? extends T> c) {
        return body.retainAll(c);
    }

    public boolean retainAll(OwnRows<? extends T> row) {
        return body.retainAll(row.toCollection());
    }

    public Stream<T> stream() {
        return body.stream();
    }

    public Stream<T> parallelStream() {
        return body.parallelStream();
    }

    public T[] toArray(T[] a) {
        return body.toArray(a);
    }


    /* *******************************************************************************************
     *
     *          实现类特定方法
     *
     * *******************************************************************************************
     * */


    public void ensureCapacity(int minCapacity) {
        if(body instanceof ArrayList) {
            ((ArrayList<T>) body).ensureCapacity(minCapacity);
        }
    }


    public void rimToSize() {
        if(body instanceof ArrayList) {
            ((ArrayList<T>) body).trimToSize();
        }
    }


    public boolean addIfAbsent(T obj) {
        if(body instanceof CopyOnWriteArrayList) {
            return ((CopyOnWriteArrayList<T>) body).addIfAbsent(obj);
        }
        throw new InvalidOperationException("容器的实现类必须为 java.util.concurrent.CopyOnWriteArrayList " +
                "才可以调用该方法，当前容器实现类为 【" + implEnum.getImplClassname() + "】");
    }

    public int addAllIfAbsent(Collection<? extends T> cs) {
        if(body instanceof CopyOnWriteArrayList) {
            return ((CopyOnWriteArrayList<T>) body).addAllAbsent(cs);
        }
        throw new InvalidOperationException("容器的实现类必须为 java.util.concurrent.CopyOnWriteArrayList " +
                "才可以调用该方法，当前容器实现类为 【" + implEnum.getImplClassname() + "】");
    }


    public int indexOfFromCW(int index, T obj) {
        if(body instanceof CopyOnWriteArrayList) {
            return ((CopyOnWriteArrayList<T>) body).indexOf(obj, index);
        }
        throw new InvalidOperationException("容器的实现类必须为 java.util.concurrent.CopyOnWriteArrayList " +
                "才可以调用该方法，当前容器实现类为 【" + implEnum.getImplClassname() + "】");
    }

    public int lastIndexOfFromCW(int index, T obj) {
        if(body instanceof CopyOnWriteArrayList) {
            return ((CopyOnWriteArrayList<T>) body).lastIndexOf(obj, index);
        }
        throw new InvalidOperationException("容器的实现类必须为 java.util.concurrent.CopyOnWriteArrayList " +
                "才可以调用该方法，当前容器实现类为 【" + implEnum.getImplClassname() + "】");
    }


    public Iterator<T> descendingIterator() {
        if(body instanceof LinkedList) {
            return ((LinkedList<T>) body).descendingIterator();
        }
        throw new InvalidOperationException("容器的实现类必须为 java.util.LinkedList " +
                "才可以调用该方法，当前容器实现类为 【" + implEnum.getImplClassname() + "】");
    }


    public void addLast(T obj) {
        if(body instanceof LinkedList) {
            ((LinkedList<T>) body).addLast(obj);
            return;
        }
        this.body.add(obj);
    }


    public T element() {
        if(body instanceof LinkedList) {
            return ((LinkedList<T>) body).element();
        }
        return this.getFirst();
    }


    public T peek() {
        if(body instanceof LinkedList) {
            return ((LinkedList<T>) body).peek();
        }
        return this.getFirst();
    }


    public T peekFirst() {
        if(body instanceof LinkedList) {
            return ((LinkedList<T>) body).peekFirst();
        }
        return this.getFirst();
    }


    public T peekLast() {
        if(body instanceof LinkedList) {
            return ((LinkedList<T>) body).peekLast();
        }
        return this.getLast();
    }


    public boolean offer(T obj) {
        if(body instanceof LinkedList) {
            return ((LinkedList<T>) body).offer(obj);
        }
        return body.add(obj);
    }


    public boolean offerFirst(T obj) {
        if(body instanceof LinkedList) {
            return ((LinkedList<T>) body).offerFirst(obj);
        }
        this.body.add(0, obj);
        return true;
    }


    public boolean offerLast(T obj) {
        if(body instanceof LinkedList) {
            return ((LinkedList<T>) body).offerLast(obj);
        }
        return body.add(obj);
    }


    public T poll() {
        if(body instanceof LinkedList) {
            return ((LinkedList<T>) body).poll();
        }
        return (body.isEmpty()) ? null : body.get(0);
    }

    public T pollFirst() {
        if(body instanceof LinkedList) {
            return ((LinkedList<T>) body).pollFirst();
        }
        return (body.isEmpty()) ? null : body.get(0);
    }


    public T pollLast() {
        if(body instanceof LinkedList) {
            return ((LinkedList<T>) body).pollLast();
        }
        return (body.isEmpty()) ? null : this.getLast();
    }


    public void push(T obj) {
        if(body instanceof LinkedList) {
            ((LinkedList<T>) body).push(obj);
            return;
        }
        body.add(obj);
    }


    public T pop() {
        if(body instanceof LinkedList) {
            return ((LinkedList<T>) body).pop();
        }
        return (body.isEmpty()) ? null : body.get(0);
    }


    /* *******************************************************************************************
     *
     *          私有逻辑
     *
     * *******************************************************************************************
     * */

    @Note("List 集合实现类枚举不能为 null")
    private void private_implNotNull(ListEnum impl) {
        if(impl == null) throw new IllegalArgumentException("若想指定内部容器的初始化的实现类型，则 ListEnum 枚举不能为 null");
    }

    @Note("检查集合进行添加操作时，指定插入位置的索引值是否合法，不能小于 0 和大于集合长度")
    private void private_checkAllowAddIndex(int index) {
        if(index < 0 || index > body.size()) {
            throw new IllegalArgumentException("添加操作，最大可用索引为 0 ~ " +
                    body.size() + "，而输入的 index 为 " + index);
        }
    }

    @Note("检查集合进行查询、获取操作时，其输入的索引值是否合法，不能小于 0 和大于等于集合长度")
    private void private_checkReadIndex(int index) {
        if(index < 0 || index >= body.size()) {
            throw new IllegalArgumentException("只读操作，最大可用索引为 0 ~ " +
                    (body.size() - 1) + "，而输入的 index 为 " + index);
        }
    }

    @Note("根据 implEnum 构建 List 集合，然后拷贝数组 array 里的元素到该 List 集合里，" +
            "最后根据 useComparator 绝对要不要用 Comparator 排序比较器对集合内容进行排序")
    private void private_initBody(T[] array, Comparator<? super T> c, boolean useComparator, ListEnum implEnum) {
        this.implEnum = (implEnum == null) ? this.implEnum : implEnum;
        if(array == null || array.length == 0) {
            this.body = this.implEnum.newList(0);
            return;
        }
        this.body = this.implEnum.newList(array);
        if(!body.isEmpty() && useComparator) this.body.sort(c);
    }


    @Note("根据 implEnum 构建 List 集合，然后拷贝可迭代容器 iterable 里的元素到该 List 集合里，" +
            "最后根据 useComparator 绝对要不要用 Comparator 排序比较器对集合内容进行排序")
    private void private_initBody(Iterable<? extends T> iterable, Comparator<? super T> c, boolean useComparator, ListEnum implEnum) {
        this.implEnum = (implEnum == null) ? this.implEnum : implEnum;
        this.body = this.implEnum.newList(iterable);
        if(!body.isEmpty() && useComparator) this.body.sort(c);
    }

    @Note("用于验证一个范围的起始索引和结束索引的合法性")
    protected void protected_checkRangeIndex(int fromIndex, int toIndex) {
        if(fromIndex > toIndex) {
            throw new IllegalArgumentException("起始索引 " + fromIndex + " 不能大于结束索引 " + toIndex);
        }
        private_checkReadIndex(fromIndex);
        private_checkAllowAddIndex(toIndex);
    }

    @Note("Java 自带序列化的，自定义 writeObject 序列化方法")
    private void writeObject(ObjectOutputStream output) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Method writeObjectMethod = this.body.getClass().getDeclaredMethod("writeObject", ObjectOutputStream.class);
        writeObjectMethod.setAccessible(true);
        writeObjectMethod.invoke(this.body, output);
    }

    @Note("Java 自带序列化的，自定义 readObject 反序列化方法")
    private void readObject(ObjectInputStream input) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Method readObjectMethod = this.body.getClass().getDeclaredMethod("readObject", ObjectInputStream.class);
        readObjectMethod.setAccessible(true);
        readObjectMethod.invoke(this.body, input);
    }
}
